#ifndef __ELF64_H
#define __ELF64_H
#include "stdtype.h"
#include "compiler.h"
#include "mstring.h"
#include "bit.h"

#define ELF64_BIGENDIAN MX_CPU_BIGENDIAN

/// elf64 header

/* mx_elf64_ehdr_t.e_ident.mag* values */
#define ELF64_EHDR_E_IDENT_MAG0 0x7f            // e_ident[0]
#define ELF64_EHDR_E_IDENT_MAG1 'E'             // e_ident[1]
#define ELF64_EHDR_E_IDENT_MAG2 'L'             // e_ident[2]
#define ELF64_EHDR_E_IDENT_MAG3 'F'             // e_ident[3]
/* mx_elf64_ehdr_t.e_ident.class values */
#define ELF64_EHDR_E_IDENT_CLASS32 1            // 32-bit objects
#define ELF64_EHDR_E_IDENT_CLASS64 2            // 64-bit objects
/* mx_elf64_ehdr_t.e_ident.data values */
#define ELF64_EHDR_E_IDENT_DATA2LSB 1           // Object file data structures are little-endian
#define ELF64_EHDR_E_IDENT_DATA2MSB 2           // Object file data structures are big-endian
/* mx_elf64_ehdr_t.e_ident.osabi values */
#define ELF64_EHDR_E_IDENT_OSABI_SYSV 0         // System V ABI
#define ELF64_EHDR_E_IDENT_OSABI_HPUX 1         // HP-UX operating system
#define ELF64_EHDR_E_IDENT_OSABI_STANDALONE 255 // Standalone (embedded) application

/* mx_elf64_ehdr_t.e_type values */
#define ELF64_EHDR_E_TYPE_NONE 0                // No file type
#define ELF64_EHDR_E_TYPE_REL 1                 // Relocatable object file
#define ELF64_EHDR_E_TYPE_EXEC 2                // Executable file
#define ELF64_EHDR_E_TYPE_DYN 3                 // Shared object file
#define ELF64_EHDR_E_TYPE_CORE 4                // Core file
#define ELF64_EHDR_E_TYPE_LOOS 0xFE00           // Environment-specific use
#define ELF64_EHDR_E_TYPE_HIOS 0xFEFF           // Environment-specific use
#define ELF64_EHDR_E_TYPE_LOPROC 0xFF00         // Processor-specific use
#define ELF64_EHDR_E_TYPE_HIPROC 0xFFFF         // Processor-specific use

/* elf64 header descriptor */
typedef struct
{
    struct
    {
        mx_uint8_t mag0;                        // 0x7f
        mx_uint8_t mag1;                        // 'E'
        mx_uint8_t mag2;                        // 'L'
        mx_uint8_t mag3;                        // 'F'
        mx_uint8_t class;                       // file class
        mx_uint8_t data;                        // data encoding, MSB/LSB
        mx_uint8_t version;                     // file version, must be EV_CURRENT(1)
        mx_uint8_t osabi;                       // OS/ABI identification
        mx_uint8_t abiversion;                  // ABI version
        mx_uint8_t pad;                         // Start of padding bytes
        mx_uint8_t reserved[6];
    } e_ident;                                  // totle size = 16bytes
    mx_uint16_t e_type;                         // Object file type
    mx_uint16_t e_machine;                      // Machine type
    mx_uint32_t e_version;                      // Object file version
    mx_uint64_t e_entry;                        // Entry point address
    mx_uint64_t e_phoff;                        // Program header offset
    mx_uint64_t e_shoff;                        // Section header offset
    mx_uint32_t e_flags;                        // Processor-specific flags
    mx_uint16_t e_ehsize;                       // ELF header size
    mx_uint16_t e_phentsize;                    // Size of program header entry
    mx_uint16_t e_phnum;                        // Number of program header entries
    mx_uint16_t e_shentsize;                    // Size of section header entry
    mx_uint16_t e_shnum;                        // Number of section header entries
    mx_uint16_t e_shstrndx;                     // Section name string table index
} mx_packed mx_elf64_ehdr_t, *mx_elf64_ehdr_pt;

/// section header

/* mx_elf64_shdr_t.sh_type values */
#define ELF64_SHDR_SH_TYPE_NULL 0               // Marks an unused section header
#define ELF64_SHDR_SH_TYPE_PROGBITS 1           // Contains information defined by the program
#define ELF64_SHDR_SH_TYPE_SYMTAB 2             // Contains a linker symbol table
#define ELF64_SHDR_SH_TYPE_STRTAB 3             // Contains a string table
#define ELF64_SHDR_SH_TYPE_RELA 4               // Contains `Rela` type relocation entries
#define ELF64_SHDR_SH_TYPE_HASH 5               // Contains a symbol hash table
#define ELF64_SHDR_SH_TYPE_DYNAMIC 6            // Contains dynamic linking tables
#define ELF64_SHDR_SH_TYPE_NOTE 7               // Contains note information
#define ELF64_SHDR_SH_TYPE_NOBITS 8             // Contains uninitialized space; does not occupy any space in the file
#define ELF64_SHDR_SH_TYPE_REL 9                // Contains `Rel` type relocation entries
#define ELF64_SHDR_SH_TYPE_SHLIB 10             // Reserved
#define ELF64_SHDR_SH_TYPE_DYNSYM 11            // Contains a dynamic loader symbol table
#define ELF64_SHDR_SH_TYPE_LOOS 0x60000000      // Environment-specific use
#define ELF64_SHDR_SH_TYPE_HIOS 0x6FFFFFFF      // Environment-specific use
#define ELF64_SHDR_SH_TYPE_LOPROC 0x70000000    // Processor-specific use
#define ELF64_SHDR_SH_TYPE_HIPROC 0x7FFFFFFF    // Processor-specific use

/* mx_elf64_shdr_t.sh_flags values */
#define ELF64_SHDR_SH_FLAGS_WRITE_MASK 0x1      // Section contains writable data
#define ELF64_SHDR_SH_FLAGS_ALLOC_MASK 0x2      // Section is allocated in memory image of program
#define ELF64_SHDR_SH_FLAGS_EXECINSTR_MASK 0x4  // Section contains executable instructions
#define ELF64_SHDR_SH_FLAGS_MASKOS 0x0F000000   // Environment-specific use
#define ELF64_SHDR_SH_FLAGS_MASKPROC 0xF0000000 // Processor-specific use

/* section header descriptor */
typedef struct
{
    mx_uint32_t sh_name;                        // Section name
    mx_uint32_t sh_type;                        // Section type
    mx_uint64_t sh_flags;                       // Section attributes
    mx_uint64_t sh_addr;                        // Virtual address in memory
    mx_uint64_t sh_offset;                      // Offset in file
    mx_uint64_t sh_size;                        // Size of section
    mx_uint32_t sh_link;                        // Link to other section
    mx_uint32_t sh_info;                        // Miscellaneous information
    mx_uint64_t sh_addralign;                   // Address alignment boundary
    mx_uint64_t sh_entsize;                     // Size of entries, if section has table
} mx_packed mx_elf64_shdr_t, *mx_elf64_shdr_pt;

/// symbol table

/* mx_elf64_sym_t.st_info.bind values */
#define ELF64_SYM_ST_INFO_BIND_LOCAL 0          // Not visible outside the object file 
#define ELF64_SYM_ST_INFO_BIND_GLOBAL 1         // Global symbol, visible to all object files
#define ELF64_SYM_ST_INFO_BIND_WEAK 2           // Global scope, but with lower precedence than global symbols
#define ELF64_SYM_ST_INFO_BIND_LOOS 10          // Environment-specific use
#define ELF64_SYM_ST_INFO_BIND_HIOS 12          // Environment-specific use
#define ELF64_SYM_ST_INFO_BIND_LOPROC 13        // Processor-specific use
#define ELF64_SYM_ST_INFO_BIND_HIPROC 15        // Processor-specific use
/* mx_elf64_sym_t.st_info.type values */
#define ELF64_SYM_ST_INFO_TYPE_NOTYPE 0         // No type specified (e.g., an absolute symbol)
#define ELF64_SYM_ST_INFO_TYPE_OBJECT 1         // Data object (variable)
#define ELF64_SYM_ST_INFO_TYPE_FUNC 2           // Function entry point
#define ELF64_SYM_ST_INFO_TYPE_SECTION 3        // Symbol is associated with a section
#define ELF64_SYM_ST_INFO_TYPE_FILE 4           // Source file associated with the object file
#define ELF64_SYM_ST_INFO_TYPE_LOOS 10          // Environment-specific use
#define ELF64_SYM_ST_INFO_TYPE_HIOS 12          // Environment-specific use
#define ELF64_SYM_ST_INFO_TYPE_LOPROC 13        // Processor-specific use
#define ELF64_SYM_ST_INFO_TYPE_HIPROC 15        // Processor-specific use

/* mx_elf64_sym_t.st_shndx values (Special Section Indices) */
#define ELF64_SYM_ST_SHNDX_UNDEF 0              // Used to mark an undefined or meaningless section reference
#define ELF64_SYM_ST_SHNDX_LOPROC 0xFF00        // Processor-specific use
#define ELF64_SYM_ST_SHNDX_HIPROC 0xFF1F        // Processor-specific use
#define ELF64_SYM_ST_SHNDX_LOOS 0xFF20          // Environment-specific use
#define ELF64_SYM_ST_SHNDX_HIOS 0xFF3F          // Environment-specific use
#define ELF64_SYM_ST_SHNDX_ABS 0xFFF1           // Indicates that the corresponding reference is an absolute value
#define ELF64_SYM_ST_SHNDX_COMMON 0xFFF2        // Indicates a symbol that has been declared as a common block (Fortran COMMON or C tentative declaration)

/* symbol table descriptor, entry */
typedef struct
{
    mx_uint32_t st_name;                        // Symbol name
    struct {
        mx_uint8_t type:4;                      // symbol type
        mx_uint8_t bind:4;                      // symbol bind
    } st_info;
    mx_uint8_t st_other;                        // Reserved
    mx_uint16_t st_shndx;                       // Section table index
    mx_uint64_t st_value;                       // Symbol value
    mx_uint64_t st_size;                        // Size of object (e.g., common)
} mx_packed mx_elf64_sym_t, *mx_elf64_sym_pt;

/// relocation table

/* mx_elf64_rel_t.r_info operations */
#define ELF64_REL_R_INFO_INDEX(r_info) ((r_info) >> 32)
#define ELF64_REL_R_INFO_TYPE(r_info) ((r_info) & 0xffffffffL)
#define ELF64_REL_R_INFO(index, type) (((index) << 32) + ((type) & 0xffffffffL))

/* relocation table descriptor, for mx_elf64_shdr_t.sh_type == SH_TYPE_REL */
typedef struct
{
    mx_uint64_t r_offset;                       // Address of reference
    struct
    {
#if (ELF64_BIGENDIAN)
        mx_uint32_t index;                      // specify a symbol's index that in symbol table
        mx_uint32_t type;                       // relocation type
#else
        mx_uint32_t type;
        mx_uint32_t index;
#endif
    } r_info;
} mx_packed mx_elf64_rel_t, *mx_elf64_rel_pt;

/* relocation table descriptor, for mx_elf64_shdr_t.sh_type == SH_TYPE_RELA */
typedef struct
{
    mx_uint64_t r_offset;
    struct
    {
#if (ELF64_BIGENDIAN)
        mx_uint32_t index;
        mx_uint32_t type;
#else
        mx_uint32_t type;
        mx_uint32_t index;
#endif
    } r_info;
    mx_int64_t r_addend;                        // Constant part of expression
} mx_packed mx_elf64_rela_t, *mx_elf64_rela_pt;

/// dynamic table

/* mx_elf64_dyn_t.d_tag values */
                                                //   d_un
#define ELF64_DYN_D_TAG_NULL 0                  // (ignored) Marks the end of the dynamic array
#define ELF64_DYN_D_TAG_NEEDED 1                // (d_val) The string table offset of the name of a needed library
#define ELF64_DYN_D_TAG_PLTRELSZ 2              // (d_val) Total size, in bytes, of the relocation entries associated with the procedure linkage table
#define ELF64_DYN_D_TAG_PLTGOT 3                // (d_ptr) Contains an address associated with the linkage table The specific meaning of this field is processor-dependent
#define ELF64_DYN_D_TAG_HASH 4                  // (d_ptr) Address of the symbol hash table, described below
#define ELF64_DYN_D_TAG_STRTAB 5                // (d_ptr) Address of the dynamic string table
#define ELF64_DYN_D_TAG_SYMTAB 6                // (d_ptr) Address of the dynamic symbol table
#define ELF64_DYN_D_TAG_RELA 7                  // (d_ptr) Address of a relocation table with Elf64_Rela entries
#define ELF64_DYN_D_TAG_RELASZ 8                // (d_val) Total size, in bytes, of the DT_RELA relocation table
#define ELF64_DYN_D_TAG_RELAENT 9               // (d_val) Size, in bytes, of each DT_RELA relocation entry
#define ELF64_DYN_D_TAG_STRSZ 10                // (d_val) Total size, in bytes, of the string table
#define ELF64_DYN_D_TAG_SYMENT 11               // (d_val) Size, in bytes, of each symbol table entry
#define ELF64_DYN_D_TAG_INIT 12                 // (d_ptr) Address of the initialization function
#define ELF64_DYN_D_TAG_FINI 13                 // (d_ptr) Address of the termination function
#define ELF64_DYN_D_TAG_SONAME 14               // (d_val) The string table offset of the name of this shared object
#define ELF64_DYN_D_TAG_RPATH 15                // (d_val) The string table offset of a shared library search path string
#define ELF64_DYN_D_TAG_SYMBOLIC 16             // (ignored) The presence of this dynamic table entry modifies the symbol resolution algorithm for references within the library. Symbols defined within the library are used to resolve references before the dynamic linker searches the usual search path
#define ELF64_DYN_D_TAG_REL 17                  // (d_ptr) Address of a relocation table with Elf64_Rel entries
#define ELF64_DYN_D_TAG_RELSZ 18                // (d_val) Total size, in bytes, of the DT_REL relocation table
#define ELF64_DYN_D_TAG_RELENT 19               // (d_val) Size, in bytes, of each DT_REL relocation entry
#define ELF64_DYN_D_TAG_PLTREL 20               // (d_val) Type of relocation entry used for the procedure linkage table The d_val member contains either DT_REL or DT_RELA
#define ELF64_DYN_D_TAG_DEBUG 21                // (d_ptr) Reserved for debugger use
#define ELF64_DYN_D_TAG_TEXTREL 22              // (ignored) The presence of this dynamic table entry signals that the relocation table contains relocations for a non-writable segment
#define ELF64_DYN_D_TAG_JMPREL 23               // (d_ptr) Address of the relocations associated with the procedure linkage table
#define ELF64_DYN_D_TAG_BIND_NOW 24             // (ignored) The presence of this dynamic table entry signals that the dynamic loader should process all relocations for this object before transferring control to the program
#define ELF64_DYN_D_TAG_INIT_ARRAY 25           // (d_ptr) Pointer to an array of pointers to initialization functions
#define ELF64_DYN_D_TAG_FINI_ARRAY 26           // (d_ptr) Pointer to an array of pointers to termination functions
#define ELF64_DYN_D_TAG_INIT_ARRAYSZ 27         // (d_val) Size, in bytes, of the array of initialization functions
#define ELF64_DYN_D_TAG_FINI_ARRAYSZ 28         // (d_val) Size, in bytes, of the array of termination functions
#define ELF64_DYN_D_TAG_LOOS 0x60000000         // Defines a range of dynamic table tags that are reserved for environment-specific use
#define ELF64_DYN_D_TAG_HIOS 0x6FFFFFFF         // Defines a range of dynamic table tags that are reserved for environment-specific use
#define ELF64_DYN_D_TAG_LOPROC 0x70000000       // Defines a range of dynamic table tags that are reserved for processor-specific use
#define ELF64_DYN_D_TAG_HIPROC 0x7FFFFFFF       // Defines a range of dynamic table tags that are reserved for processor-specific use

/* dynamic table descripto, entry, for mx_elf64_shdr_t.sh_type == SH_TYPE_DYNAMIC */
typedef struct
{
    mx_int64_t d_tag;                           // controls the interpretation of `d_un`
    union
    {
        mx_uint64_t d_val;
        mx_uint64_t d_ptr;
    } d_un;
} mx_packed mx_elf64_dyn_t, *mx_elf64_dyn_pt;

/// program header

/* mx_elf64_phdr_t.p_type values */
#define ELF64_PHDR_P_TYPE_NULL 0                // Unused entry
#define ELF64_PHDR_P_TYPE_LOAD 1                // Loadable segment
#define ELF64_PHDR_P_TYPE_DYNAMIC 2             // Dynamic linking tables
#define ELF64_PHDR_P_TYPE_INTERP 3              // Program interpreter path name
#define ELF64_PHDR_P_TYPE_NOTE 4                // Note sections
#define ELF64_PHDR_P_TYPE_SHLIB 5               // Reserved
#define ELF64_PHDR_P_TYPE_PHDR 6                // Program header table
#define ELF64_PHDR_P_TYPE_LOOS 0x60000000       // Environment-specific use
#define ELF64_PHDR_P_TYPE_HIOS 0x6FFFFFFF       // Environment-specific use
#define ELF64_PHDR_P_TYPE_LOPROC 0x70000000     // Processor-specific use
#define ELF64_PHDR_P_TYPE_HIPROC 0x7FFFFFFF     // Processor-specific use

/* mx_elf64_phdr_t.p_flags values */
#define ELF64_PHDR_P_FLAGS_X 0x1                // Execute permission
#define ELF64_PHDR_P_FLAGS_W 0x2                // Write permission
#define ELF64_PHDR_P_FLAGS_R 0x4                // Read permission
#define ELF64_PHDR_P_FLAGS_MASKOS 0x00FF0000    // These flag bits are reserved for environment-specific use
#define ELF64_PHDR_P_FLAGS_MASKPROC 0xFF000000  // These flag bits are reserved for processor-specific use

/* program header descriptor, entry */
typedef struct
{
    mx_uint32_t p_type;                         // Type of segment
    mx_uint32_t p_flags;                        // Segment attributes
    mx_uint64_t p_offset;                       // Offset in file
    mx_uint64_t p_vaddr;                        // Virtual address in memory
    mx_uint64_t p_paddr;                        // Reserved
    mx_uint64_t p_filesz;                       // Size of segment in file
    mx_uint64_t p_memsz;                        // Size of segment in memory
    mx_uint64_t p_align;                        // Alignment of segment
} mx_packed mx_elf64_phdr_t, *mx_elf64_phdr_pt;

/// applications

/* get the elf-header by object */
#define mx_elf64_obj_ehdr(obj) ((mx_elf64_ehdr_pt)(obj))
/* get the program-header by object */
#define mx_elf64_obj_phdr(obj) ((mx_elf64_phdr_pt)(((mx_elf64_ehdr_pt)(obj))->e_phoff + (mx_uint64_t)(obj)))
/* get the section-header by object */
#define mx_elf64_obj_shdr(obj) ((mx_elf64_shdr_pt)(((mx_elf64_ehdr_pt)(obj))->e_shoff + (mx_uint64_t)(obj)))
/* get the section-header (.shstrtab) that for `sh_name` by object */
#define mx_elf64_obj_shdr_shstrtab(obj) (&(mx_elf64_obj_shdr(obj)[mx_elf64_obj_ehdr(obj)->e_shstrndx]))
/* get the section (context) by section-header */
#define mx_elf64_obj_s(obj, shdr) ((shdr)->sh_offset + (mx_uint64_t)(obj))
/* get the section name by `object and shdr` */
#define mx_elf64_obj_shdr_name(obj, shdr) ((mx_string_pt)(mx_elf64_obj_s(obj, mx_elf64_obj_shdr_shstrtab(obj)) + (shdr)->sh_name))

/**
 * iterate all program-header by object
 * @prototype: void mx_elf64_obj_phdr_foreach(mx_uint8_pt obj, <p>, {...});
 */
#define mx_elf64_obj_phdr_foreach(obj, p, ...) {    \
    mx_elf64_ehdr_pt obj_phdr_foreach_pehdr = (mx_elf64_ehdr_pt)(obj);    \
    mx_elf64_phdr_pt obj_phdr_foreach_first = (mx_elf64_phdr_pt)(obj_phdr_foreach_pehdr->e_phoff + (mx_uint64_t)(obj));   \
    for (mx_elf64_phdr_pt p = obj_phdr_foreach_first;   \
         p < &obj_phdr_foreach_first[obj_phdr_foreach_pehdr->e_phnum];  \
         p ++)   \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * iterate all section-header by object
 * @prototype: void mx_elf64_obj_shdr_foreach(mx_uint8_pt obj, <s>, {...});
 */
#define mx_elf64_obj_shdr_foreach(obj, s, ...) {    \
    mx_elf64_ehdr_pt obj_shdr_foreach_pehdr = (mx_elf64_ehdr_pt)(obj);    \
    mx_elf64_shdr_pt obj_shdr_foreach_first = (mx_elf64_shdr_pt)(obj_shdr_foreach_pehdr->e_shoff + (mx_uint64_t)(obj));   \
    for (mx_elf64_shdr_pt s = obj_shdr_foreach_first;   \
         s < &obj_shdr_foreach_first[obj_shdr_foreach_pehdr->e_shnum];  \
         s ++)   \
    {   \
        __VA_ARGS__ \
    }   \
}

/* the descriptor for elf analysis */
typedef struct
{
    mx_uint8_t bitwide;                         // ehdr.e_ident.class
    mx_elf64_ehdr_pt ehdr;
    struct
    {
        mx_elf64_phdr_pt start;
        mx_uint16_t count;
    } phdr;                                     // program-header
    struct
    {
        mx_elf64_shdr_pt start;
        mx_uint16_t count;
        mx_uint64_t namebase;
    } shdr;                                     // section-header
    struct
    {
        mx_elf64_sym_pt start;
        mx_uint64_t count;
        mx_uint64_t namebase;                   // from .strtab
    } symtab;                                   // symbol of .symtab
    struct
    {
        mx_elf64_sym_pt start;
        mx_uint64_t count;
        mx_uint64_t namebase;                   // from .dynstr
    } dynsym;                                   // symbol of .dynsym
    struct
    {
        mx_elf64_dyn_pt start;
        mx_uint64_t count;
    } dynamic;                                  // dynamic table
} mx_elf64_analysis_t, *mx_elf64_analysis_pt;

/* get the physical address of section by section-header */
#define mx_elf64_section_paddr(analysis, shdr) ((shdr)->sh_offset + (mx_uint64_t)(analysis)->ehdr)
/* get the virtual address of section */
#define mx_elf64_section_vaddr(shdr) ((shdr)->sh_addr)
/* get the entry count of section by section-header */
#define mx_elf64_s_entcount(shdr) ((shdr)->sh_size / (shdr)->sh_entsize)
/* get the section name by `analysis and shdr` */
#define mx_elf64_shdr_name(analysis, shdr) ((mx_string_pt)((analysis)->shdr.namebase + shdr->sh_name))
/* get the symbol name of .symtab by `analysis and symbol` */
#define mx_elf64_symtab_name(analysis, sym) ((mx_string_pt)((analysis)->symtab.namebase + sym->st_name))
/* get the symbol name of .dynsym by `analysis and symbol` */
#define mx_elf64_dynsym_name(analysis, sym) ((mx_string_pt)((analysis)->dynsym.namebase + sym->st_name))
/* get the virtual address of symbol (not the phycial addres in file! and can't get the value of symbol by it) */
#define mx_elf64_symbol_vaddr(analysis, sym) (((sym)->st_shndx == ELF64_SYM_ST_SHNDX_ABS || (analysis)->ehdr->e_type == ELF64_EHDR_E_TYPE_EXEC || (analysis)->ehdr->e_type == ELF64_EHDR_E_TYPE_DYN)  \
                                              ? (sym)->st_value : ((sym)->st_value + (analysis)->shdr.start[(sym)->st_shndx].sh_addr))
/* get the symbol entry corresponding to the relocation item,
   the parameter `shdr` is that the section-header contains the rel entry, applicable to rela or rel entry */
#define mx_elf64_relx_symbol(analysis, shdr, rel) (&((mx_elf64_sym_pt)mx_elf64_section_paddr(analysis, &(analysis)->shdr.start[(shdr)->sh_link]))[(rel)->r_info.index])
/* get the virtual address which need to repair by relocation entry,
   the parameter `shdr` is that the section-header contains the rel entry, applicable to rela or rel entry */
#define mx_elf64_relx_repvaddr(analysis, shdr, rel) (((analysis)->ehdr->e_type == ELF64_EHDR_E_TYPE_EXEC || (analysis)->ehdr->e_type == ELF64_EHDR_E_TYPE_DYN) ?    \
                                                     (rel)->r_offset : mx_elf64_section_vaddr(&(analysis)->shdr.start[(shdr)->sh_info]) + (rel)->r_offset)
/* get the virtual address of the symbol corresponding to the relocation item,
   the parameter `shdr` is that the section-header contains the rel entry */
#define mx_elf64_rela_symvaddr(analysis, shdr, rel) (mx_elf64_symbol_vaddr(analysis, mx_elf64_relx_symbol(analysis, shdr, rel)) + (rel)->r_addend)

/**
 * iterate all program-header by analysis
 * @prototype: void mx_elf64_phdr_foreach(mx_elf64_analysis_pt analysis, <p>, {...});
 */
#define mx_elf64_phdr_foreach(analysis, p, ...) {   \
    for (mx_elf64_phdr_pt p = (analysis)->phdr.start;   \
         p < &(analysis)->phdr.start[(analysis)->phdr.count];  \
         p ++)   \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * iterate all section-header by analysis
 * @prototype: void mx_elf64_shdr_foreach(mx_elf64_analysis_pt analysis, <s>, {...});
 */
#define mx_elf64_shdr_foreach(analysis, s, ...) {    \
    for (mx_elf64_shdr_pt s = (analysis)->shdr.start;   \
         s < &(analysis)->shdr.start[(analysis)->shdr.count];  \
         s ++)   \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * iterate all symbols of .symtab by analysis
 * @prototype: void mx_elf64_symtab_foreach(mx_elf64_analysis_pt analysis, <s>, {...});
 */
#define mx_elf64_symtab_foreach(analysis, s, ...) { \
    for (mx_elf64_sym_pt s = (analysis)->symtab.start; \
         s < &(analysis)->symtab.start[(analysis)->symtab.count]; \
         s ++)   \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * iterate all symbols of .dynsym by analysis
 * @prototype: void mx_elf64_dynsym_foreach(mx_elf64_analysis_pt analysis, <s>, {...});
 */
#define mx_elf64_dynsym_foreach(analysis, s, ...) { \
    for (mx_elf64_sym_pt s = (analysis)->dynsym.start; \
         s < &(analysis)->dynsym.start[(analysis)->dynsym.count]; \
         s ++)   \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * iterate rel-entries by `analysis and shdr(sh_type == REL)`
 * @prototype: void mx_elf64_rel_foreach(mx_elf64_analysis_pt analysis, mx_elf64_shdr_pt shdr, <r>, {...});
 */
#define mx_elf64_rel_foreach(analysis, shdr, r, ...) {  \
    mx_elf64_rel_pt rel_foreach_start = (mx_elf64_rel_pt)mx_elf64_obj_s((analysis)->ehdr, (shdr)); \
    mx_elf64_rel_pt rel_foreach_end = &rel_foreach_start[mx_elf64_s_entcount((shdr))]; \
    for (mx_elf64_rel_pt r = rel_foreach_start; \
         r < rel_foreach_end;   \
         r ++)  \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * iterate rela-entries by `analysis and shdr(sh_type == RELA)`
 * @prototype: void mx_elf64_rela_foreach(mx_elf64_analysis_pt analysis, mx_elf64_shdr_pt shdr, <r>, {...});
 */
#define mx_elf64_rela_foreach(analysis, shdr, r, ...) {  \
    mx_elf64_rela_pt rela_foreach_start = (mx_elf64_rela_pt)mx_elf64_obj_s((analysis)->ehdr, (shdr)); \
    mx_elf64_rela_pt rela_foreach_end = &rela_foreach_start[mx_elf64_s_entcount((shdr))]; \
    for (mx_elf64_rela_pt r = rela_foreach_start; \
         r < rela_foreach_end;   \
         r ++)  \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * iterate dynamic-entries by analysis
 * @prototype: void mx_elf64_dynamic_foreach(mx_elf64_analysis_pt analysis, <d>, {...});
 */
#define mx_elf64_dynamic_foreach(analysis, d, ...) {    \
    for (mx_elf64_dyn_pt d = (analysis)->dynamic.start; \
         d < &(analysis)->dynamic.start[(analysis)->dynamic.count]; \
         d ++)  \
    {   \
        __VA_ARGS__ \
    }   \
}

/**
 * check whether the elf-object is validity
 * @param pobj: the start address of elf-object
 * @return: the result of check
 */
static mx_bool_t mx_elf64_validity (mx_uint8_pt pobj)
{
    mx_elf64_ehdr_pt pehdr = (mx_elf64_ehdr_pt)pobj;
    /* magic check */
    if (pehdr->e_ident.mag0 != ELF64_EHDR_E_IDENT_MAG0 ||
        pehdr->e_ident.mag1 != ELF64_EHDR_E_IDENT_MAG1 ||
        pehdr->e_ident.mag2 != ELF64_EHDR_E_IDENT_MAG2 ||
        pehdr->e_ident.mag3 != ELF64_EHDR_E_IDENT_MAG3)
    {
        return mx_false;
    }
    /* class check */
    if (pehdr->e_ident.class != ELF64_EHDR_E_IDENT_CLASS32 &&
        pehdr->e_ident.class != ELF64_EHDR_E_IDENT_CLASS64)
    {
        return mx_false;
    }
    return mx_true;
}

/**
 * initialize the elf analyzer
 * @param pana: the descriptor of elf analyzer
 * @param pobj: the start address of elf-object
 * @return: whether the elf is effective
 */
static mx_bool_t mx_elf64_analysis_init (mx_elf64_analysis_pt pana, mx_uint8_pt pobj)
{
    mx_bool_t ret = mx_elf64_validity(pobj);

    if (ret)
    {
        /* elf header, its also the start address of elf */
        pana->ehdr = mx_elf64_obj_ehdr(pobj);
        /* the bitwide of elf */
        pana->bitwide = pana->ehdr->e_ident.class == ELF64_EHDR_E_IDENT_CLASS32 ? 32 : 64;
        /* program header entries */
        pana->phdr.start = mx_elf64_obj_phdr(pobj);
        pana->phdr.count = pana->ehdr->e_phnum;
        /* section header entries */
        pana->shdr.start = mx_elf64_obj_shdr(pobj);
        pana->shdr.count = pana->ehdr->e_shnum;
        pana->shdr.namebase = mx_elf64_obj_s(pobj, mx_elf64_obj_shdr_shstrtab(pobj));
        /* match the default section header in elf by `sh_type` */
        mx_elf64_obj_shdr_foreach(pobj, shdr, {
            switch (shdr->sh_type)
            {
            case ELF64_SHDR_SH_TYPE_SYMTAB:
                pana->symtab.start = (mx_elf64_sym_pt)mx_elf64_obj_s(pobj, shdr);
                pana->symtab.count = mx_elf64_s_entcount(shdr);
                pana->symtab.namebase = mx_elf64_obj_s(pobj, &pana->shdr.start[shdr->sh_link]);
                break;
            case ELF64_SHDR_SH_TYPE_DYNSYM:
                pana->dynsym.start = (mx_elf64_sym_pt)mx_elf64_obj_s(pobj, shdr);
                pana->dynsym.count = mx_elf64_s_entcount(shdr);
                pana->dynsym.namebase = mx_elf64_obj_s(pobj, &pana->shdr.start[shdr->sh_link]);
                break;
            case ELF64_SHDR_SH_TYPE_DYNAMIC:
                pana->dynamic.start = (mx_elf64_dyn_pt)mx_elf64_obj_s(pobj, shdr);
                pana->dynamic.count = mx_elf64_s_entcount(shdr);
                break;
            }
        });
    }
    return ret;
}

/**
 * get the symbol in .symtab by name
 * @param pana: the descriptor of elf analyzer
 * @param name: the symbol name
 * @return: the symbol entry
 */
static mx_elf64_sym_pt mx_elf64_symbol_getbyname (mx_elf64_analysis_pt pana, mx_string_pt name)
{
    mx_elf64_sym_pt ret = mx_null;
    
    mx_elf64_symtab_foreach(pana, sym, {
        if (mx_strcmp(mx_elf64_symtab_name(pana, sym), name) == 0)
        {
            ret = sym;
            break;
        }
    });

    return ret;
}

#endif
